home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 February: Tool Chest / Apple Developer CD Series Tool Chest February 1996 (Apple Computer)(1996).iso / Sample Code / AOCE Sample Code / PowerTalk Access Modules / Sample CSAM / Sources / Audit.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-07-28  |  32.8 KB  |  1,102 lines  |  [TEXT/MPS ]

  1. /*                                        Audit.c                                    */
  2. #define PARANOIA    0
  3. #include <Packages.h>
  4. /*
  5.  * Audit.c
  6.  * Copyright © 1992-93 Apple Computer Inc. All Rights Reserved.
  7.  * Programmed by Martin Minow,
  8.  *    Internet:    minow@apple.com
  9.  *    AppleLink:    MINOW
  10.  * Version of January 14, 1993
  11.  *
  12.  * Edit History
  13.  *    93.01.09 MM        First public release
  14.  *    93.01.14 MM        Think and MPW generate different record sizes; a disaster if
  15.  *                    you create an Audit Record under Think and call Audit compiled
  16.  *                    under MPW. Also added a test for record sizes and included
  17.  *                    record size information in the AuditRecord.
  18.  *    93.01.21 MM        Fix bug in procedure name recognizer that failed for certain
  19.  *                    pascal functions.
  20.  *    93.06.10 MM        Cosmetic edits for Think C 6.0. No functional changes. Text
  21.  *                    was reformatted to fit into an "80-column" line.
  22.  *    93.08.14 MM        Added "logicalRAMSize to the AuditRecord. This protects
  23.  *                    Audit (somewhat) against bad data forcing address errors.
  24.  *
  25.  * This library implements a drop-in logging capability for device drivers,
  26.  * callback functions, applications, and all other Macintosh code segments.
  27.  *
  28.  * The overall philosophy is that an application or driver allocates a record in
  29.  * the system heap that contains a small preamble and one or more 48 (decimal)
  30.  * byte blocks of data. The driver logs data to the log area, where each log
  31.  * record contains an identification value and up to four longwords of data.
  32.  *
  33.  * The user would then write a log control application that can turn logging on or
  34.  * off and dump/format the data in the log. The user can also use a MacsBug dcmd
  35.  * to display the current state of the log area.
  36.  *
  37.  * To add logging to a driver, add the following variable to the driver control
  38.  * record:
  39.  *        AuditPtr            auditPtr;
  40.  * and initialize the driver log (in the driver open routine) by calling
  41.  *        devCtlEnt->auditPtr = InitAudit(
  42.  *                        gestaltSelector,
  43.  *                        nEntries,
  44.  *                        initiallyEnabled,
  45.  *                        preserveFirstFlag
  46.  *                    );
  47.  *
  48.  * The intialization routine creates an audit record in the System Heap and
  49.  * defines a gestalt selector with a pointer to the audit area. Once created,
  50.  * the record remains in the heap until the Macintosh is rebooted. The
  51.  * gestaltSelector should be unique to the driver. InitAudit need only be
  52.  * called once by any code segment that can allocate memory (i.e., by an
  53.  * application, INIT, or driver open function). Note that you do not
  54.  * need to check errors: if InitAudit fails to create (or locate) the record,
  55.  * it returns NULL and all other routines, upon receiving a NULL AuditPtr
  56.  * argument, do nothing gracefully.
  57.  *
  58.  * The log record contains a fixed header with the following information:
  59.  *
  60.  *    version.u.low        The earliest version of the AuditRecord that this instance
  61.  *                        of the library understands.
  62.  *    version.u.high        The latest version of the AuditRecord that this instance
  63.  *                        of the library understands.
  64.  *    size.auditRecord    The size of the Audit Record (excluding the dummy first
  65.  *                        entry). This protects against compiler alignment errors
  66.  *                        (which may occur if an Audit Record is created by code
  67.  *                        generated by one compiler, and accessed by code generated
  68.  *                        by a different compiler.
  69.  *    size.auditEntry        The size of the AuditQueueEntry. (See above.)
  70.  *    free.Queue            A standard O.S. queue with available data logging records.
  71.  *    data.queue            A standard O.S. queue with records waiting to be displayed
  72.  *                        or written to a file.
  73.  *    lostData            This counter is incremented when Audit function cannot
  74.  *                        obtain a record from the free queue.
  75.  *    PSN                    Awaken this process when something is stored.
  76.  *    refNum                a void * (longword) that may be used by the calling
  77.  *                        software.
  78.  *    isLogging            TRUE if logging is enabled.
  79.  *    timeAtStart            GetDateTime() when the log was created.
  80.  *    ticksAtStart        TickCount() when the log was created.
  81.  *    entries[]            A vector of AuditEntry records.
  82.  *
  83.  * The audit user accesses the log record by calling functions: it should not
  84.  * access the structure directly.
  85.  *
  86.  * Each log record is stored in a AuditEntry structure that contains the
  87.  * following information:
  88.  *    tickCount            The Ticks value at the time the data was collected.
  89.  *    lostData            This is the number of records that were "lost" before this
  90.  *                        record because there was no free entry in the queue.
  91.  *    idCode                This longword identifies the log entry (i.e. who logged it).
  92.  *    format                This longword describes the format of the log data.
  93.  *    data                This 32 byte data area contains the log. If format is zero,
  94.  *                        the data is a Str31. Otherwise, it contains up to 8
  95.  *                        (sizeof data / sizeof (long)) longwords of data.
  96.  *
  97.  * Your application or driver calls the following functions to manage the log:
  98.  *
  99.  *        AuditPtr            InitAudit(
  100.  *            OSType                gestaltSelector,
  101.  *            short                nEntries
  102.  *            Boolean                initiallyEnabled,
  103.  *            Boolean                preserveFirst
  104.  *        );
  105.  *
  106.  *    Create the log record and "publish" a Gestalt selector with a pointer to the
  107.  *    record. This is normally called by a driver when it is first opened.
  108.  *    gestaltSelector is unique to the driver. InitAudit returns a pointer to the
  109.  *    log record or NULL if it could not create the log. If the gestaltSelector was
  110.  *    already registered, it returns a pointer to the already-created log without
  111.  *    creating a new log.
  112.  *
  113.  *        void                Audit(
  114.  *            AuditPtr            auditPtr,
  115.  *            OSType                idCode,
  116.  *            unsigned long        format,
  117.  *            ...
  118.  *        );
  119.  *
  120.  *    Store data in the log. idCode identifies this log request: by convention, it
  121.  *    is an OSType. The format parameter simplifies display by defining the format
  122.  *    of the subsequent data, The remaining parameters are log dependent. If auditPtr
  123.  *    is NULL or logging disabled, the function does nothing, but does not fail.
  124.  *
  125.  *    The AuditFormat macro is used to specify the format of the stored data:
  126.  *
  127.  *        AuditFormat(arg1, arg2, ..., arg8)
  128.  *
  129.  *    where arg1, etc. is specified by one of the following constants:
  130.  *        kAuditFormatEnd            No more data
  131.  *        kAuditFormatSigned        The datum is a 32-bit signed decimal integer
  132.  *        kAuditFormatUnsigned    The datum is a 32-bit unsigned decimal integer
  133.  *        kAuditFormatHex            The datum is a 32-bit address or 4-byte character
  134.  *        kAuditFormatAddress        The datum is a 32-bit hex value (never characters)
  135.  *        kAuditFormatString        The datum is a Pascal string. This must be the last
  136.  *                                (or only) argument. The string will be truncated
  137.  *                                to fit the remaining space. I.e. if it is the only
  138.  *                                argument, a 31-byte (plus one byte count) string
  139.  *                                will be stored.
  140.  *        kAuditFormatLocation    This must be the last format, but there is no
  141.  *                                associated value. Instead, the library stores
  142.  *                                the calling function name (the MacsBug name).
  143.  *                                The name will be truncated as for kAuditFormatString.
  144.  *
  145.  * Note that AuditFormat1, AuditFormat2, etc. macros are provided to simplify
  146.  * access to AuditFormat().
  147.  *
  148.  * Important: all parameters to Audit must be longwords. Short values (such as
  149.  * Booleans and OSErr codes) must be cast to long or unsigned long:
  150.  *        Audit(auditPtr, 'Err!', AuditFormat1(kAuditFormatSigned),
  151.  *            (long) statusError);
  152.  * If you do not do this, the data may be logged incorrectly or, in extreme
  153.  * cases, your program may crash.
  154.  *
  155.  ***
  156.  *        Boolean                AuditRead(
  157.  *            AuditPtr            auditPtr,
  158.  *            AuditEntry            *thisAuditEntry
  159.  *        );
  160.  *    An application program calls AuditRead periodically to retrieve data from the
  161.  *    log.  If there is a log entry to process, it is copied to thisAuditEntry and
  162.  *    the function returns TRUE. If it returns FALSE, there is no data waiting. This
  163.  *    function should be called from the log application's event loop. AuditRead
  164.  *    manages all log queues. Note that reading the log resets the missedDataCount.
  165.  *
  166.  * Each log record is time-stamped by the following algorithm:
  167.  *        GetAuditStartTimes(auditPtr, &gTimeAtStart, &gTicksAtStart);
  168.  *        if (AuditRead(auditPtr, &thisAuditEntry)) {
  169.  *            elapsedTicks = thisAuditEntry.tickCount - gTicksAtStart;
  170.  *            SecsToDate(
  171.  *                gTimeAtStart + (elapsedTicks / 60),
  172.  *                &logEntryDateString
  173.  *            );
  174.  *            printf(, ..., date.second, elapsedTicks % 60);
  175.  *        }
  176.  * See AuditEntryFormat.c for details.
  177.  *
  178.  ***
  179.  *        AuditPtr            GetAuditPtr(
  180.  *            OSType                gestaltSelector
  181.  *        );
  182.  *
  183.  *    Return a pointer to the common storage area, if it exists. This returns NULL
  184.  *    if there is no log "registered" by that name.
  185.  *
  186.  ***
  187.  *        void                WakeUpAudit(
  188.  *            AuditPtr            auditPtr,
  189.  *            ProcessSerialNumber    *oldPSN
  190.  *        );
  191.  *
  192.  *    Wake up this process (application) when something is logged: called by:
  193.  *            GetCurrentProcess((&oldPSN);
  194.  *            WakeUpAudit(auditPtr, &oldPSN);
  195.  *            ... logging stuff ...
  196.  *            WakeUpAudit(auditPtr, &oldPSN);    // restore previous
  197.  *            ExitToShell();
  198.  *
  199.  ***
  200.  *        Boolean                EnableAudit(
  201.  *            AuditPtr            auditPtr,
  202.  *            Boolean                enableLogging
  203.  *        );
  204.  *
  205.  *    Enable or disable logging. This returns the previous logging value. At
  206.  *    initialization, a compile-time parameter sets the initial logging value.
  207.  *
  208.  ***
  209.  *        Boolean                IsAuditEnabled(
  210.  *            AuditPtr            auditPtr
  211.  *        );
  212.  *
  213.  *    Return TRUE if auditing is enabled for this audit record.
  214.  *
  215.  ***
  216.  *        void                GetAuditStartTimes(
  217.  *            AuditPtr            auditPtr,
  218.  *            unsigned long        *timeAtStart,
  219.  *            unsigned long        *ticksAtStart
  220.  *        );
  221.  *    Return the times that the log was created. Using these values, the log display
  222.  *    application can time-stamp all log entries.
  223.  *
  224.  ***
  225.  *        void                SetAuditRefNum(
  226.  *            AuditPtr            auditPtr,
  227.  *            void                *refNum
  228.  *        );
  229.  * Store a user-controlled reference value. This may be coerced to any scalar
  230.  *    value (such as a memory pointer or longword).
  231.  *
  232.  ***
  233.  *        void                *GetAuditRefNum(
  234.  *            AuditPtr            auditPtr
  235.  *        );
  236.  * Return the current user-controlled reference value. This may be coerced to any
  237.  * scalar value (such as a memory pointer or longword). This returns zero if
  238.  * auditPtr is NULL or no value had been stored.
  239.  */
  240.  
  241. #include "Audit.h"
  242. #include <Types.h>
  243. #include <Traps.h>
  244. #include <Errors.h>
  245. #include <Memory.h>
  246. #include <GestaltEqu.h>
  247. #include <OSUtils.h>
  248. #ifndef PARANOIA
  249. #define PARANOIA    0
  250. #endif
  251.  
  252. /*
  253.  * The version id's are defined as (majorVersion << 8) | minorVersion
  254.  */
  255. #define kAuditEarliestVersion    ((1 << 8) | 2)
  256. #define kAuditLatestVersion        ((1 << 8) | 2)
  257.  
  258. #ifndef TRUE
  259. #define TRUE    1
  260. #define FALSE    0
  261. #endif
  262. /*
  263.  * This is Think C specific, and possibly release dependent.
  264.  */
  265. #if defined(_Gestalt) == FALSE && defined(_GestaltDispatch)
  266. #define _Gestalt    _GestaltDispatch
  267. #endif
  268.  
  269. /*
  270.  * Note that this file contains 68000 assembly. This must be modified when
  271.  * the Audit Library is converted to Power PC.
  272.  */
  273. /*
  274.  * These values must match values in AuditDCMD.c
  275.  */
  276. #define kAuditMagicHeaderSize    7            /* Size in longwords            */
  277. #define kAuditMagicHeader_0        0x4E560000    /* First word in Gestalt result    */
  278.  
  279. #if 0
  280. /*
  281.  * The following sequence was used to create the GestaltSelector stub. It is
  282.  * never compiled directly, but was pasted into a temporary file which was
  283.  * decompiled to get the machine code. 
  284.  */
  285. static pascal OSErr    GestaltSelector(
  286.         OSType            selector,
  287.         long            *result
  288.     )
  289. {
  290.         asm {
  291.             /*
  292.              * 22 was determined by painful observation and many pleasant
  293.              * encounters with MacsBug.
  294.              */
  295.             lea            22(pc),a0        /* a0 = Address of auditRecord        */
  296.             movea.l        result,a1        /* a1 -> result                        */
  297.             move.l        a0,(a1)            /* Store result                        */
  298.         }
  299.         return (noErr);
  300. }
  301. #endif
  302.  
  303. /*
  304.  * DisableInterrupts and EnableInterrupts manipulate the 68000 status register.
  305.  * They are expanded inline. A comment consisting of *** in the left margin
  306.  * indicates code that is run with interrupts disabled.
  307.  *
  308.  * unsigned short DisableInterrupts(void) {
  309.  *        asm {
  310.  *            move        sr,d0
  311.  *            move        #0x2600,sr
  312.  *        }
  313.  */
  314. unsigned short DisableInterrupts(void) = { 0x40C0, 0x46FC, 0x2600 };
  315. #ifdef THINK_C
  316. /*
  317.  * Since Think C has a real inline assembler, we can save one or two instructions.
  318.  * Nerd fun.
  319.  */
  320. #define EnableInterrupts(saveSR) asm {            \
  321.             move        saveSR,sr                \
  322.         }
  323. #else
  324. /*
  325.  * Even though the saveSR argument is a short, we must declare the function as a
  326.  * long so that both Think and MPW generate the same code. Otherwise, Think pushes
  327.  * a short and 2(sp) addresses the wrong value. (Guess how I discovered this.)
  328.  * Using the Think C inline defined above also eliminates the problem.
  329.  *
  330.  * void EnableInterrupts(unsigned long saveSR) {
  331.  *        asm {
  332.  *            move        2(sp),sr
  333.  *        }
  334.  */
  335. void EnableInterrupts(unsigned long saveSR) = { 0x46EF, 0x0002 };
  336. #endif
  337.  
  338. /*
  339.  * This inline copies the value of A6 (the frame pointer) to D0. It is needed in
  340.  * order to get the caller's function name.
  341.  */
  342. unsigned long *GetA6(void) = { 0x200E };    /* move.l A6,D0    */
  343.  
  344. #define LOG        (*auditPtr)
  345. #define ENTRY    (entryPtr->theEntry)
  346.  
  347. static void                            StoreString(
  348.         const StringPtr        theString,        /* Source, length may be wrong    */
  349.         unsigned short        stringLength,    /* Purported actual length        */
  350.         unsigned short        maxLength,        /* Maximum destination length    */
  351.         StringPtr            result            /* Destination pointer            */
  352.     );
  353. static AuditQueueEntryPtr            GetQueueEntry(
  354.         QHdrPtr                    theQueue
  355.     );
  356. #define PutQueueEntry(theQueue, theEntry) do {                \
  357.         Enqueue((QElemPtr) &theEntry->qLink, theQueue);        \
  358.     } while (0)
  359.  
  360. static Boolean                        TrapAvailable(
  361.         short                    theTrap
  362.     );
  363. static void                            GetCallerName(
  364.         unsigned long            *destPtr,
  365.         unsigned short            maxString,
  366.         unsigned long            *a6
  367.     );
  368.  
  369. /*
  370.  * InitAudit
  371.  */
  372. AuditPtr
  373. InitAudit(
  374.         OSType                    gestaltSelector,
  375.         unsigned short            nEntries,
  376.         Boolean                    initiallyEnabled,
  377.         Boolean                    preserveFirst
  378.     )
  379. {
  380.         register unsigned long        *recordPtr;
  381.         register AuditPtr            auditPtr;
  382.         register AuditQueueEntryPtr    queueEntryPtr;
  383.         Size                        logRecordSize;
  384.         short                        i;
  385.         OSErr                        status;
  386.         
  387.         /*
  388.          * This will not compile on either Think or MPW if the sizes are correct.
  389.          * Unfortunately, sizeof cannot be incorporated into a #define, so we
  390.          * have to do this at execution time.
  391.          */
  392.         if (sizeof (AuditEntry) != kSizeofAuditEntry)
  393.             DebugStr("\pAuditEntry size error");
  394.         if (sizeof (AuditQueueEntry) != kSizeofAuditQueueEntry)
  395.             DebugStr("\pAuditQueueEntry size error");
  396.         if (sizeof (AuditRecord) != kSizeofAuditRecord)
  397.             DebugStr("\pAuditRecord size error");
  398.         /*
  399.          * Presuppose problems and exit if the system is so old that it doesn't
  400.          * even support Gestalt. This is slightly overkill, as the current
  401.          * Gestalt glue does the TrapAvailable check for us.
  402.          */
  403.         auditPtr = NULL;
  404.         if (TrapAvailable(_Gestalt) == FALSE)
  405.             goto exit;
  406.         logRecordSize = sizeof (AuditRecord)
  407.                     + (kAuditMagicHeaderSize * sizeof (long))
  408.                     + ((nEntries - 1) * sizeof (AuditQueueEntry));
  409. #if PARANOIA > 1
  410.         {
  411.             Str255                    foo;
  412.             NumToString(sizeof (AuditQueueEntry), foo);
  413.             DebugStr(foo);
  414.             NumToString(logRecordSize, foo);
  415.             DebugStr(foo);
  416.         }
  417. #endif
  418.         recordPtr = (unsigned long *) NewPtrSysClear(logRecordSize);
  419.         if (recordPtr == NULL)
  420.             goto exit;
  421.         auditPtr = (AuditPtr) &recordPtr[kAuditMagicHeaderSize];
  422.         /*
  423.          * The code for the Gestalt Selector function precedes the actual audit
  424.          * record. Gestalt calls this function when a program calls GetAuditPtr
  425.          * for our selector. The function returns the address of the AuditRecord
  426.          * as the selector value. This code will need revision when run on
  427.          * non-Motorola cpu's. See the definition of GestaltSelector above for
  428.          * the original code.
  429.          *
  430.          * asm {
  431.          *        link        a6,#0000    Create stack frame    0x4E56 0x0000
  432.          *        lea            22(pc),a0    a0 -> auditRecord    0x41FA 0x0016  
  433.          *        movea.l        8(a6),a1    a1 -> response        0x226E 0x0008
  434.          *        move.l        a0,(a1)        Store result        0x2288
  435.          *        clr.w        8(a6)        Result := noErr        0x426E 0x0010
  436.          *        unlk        a6            Delete stack frame    0x4E5E
  437.          *        movea.l        (a7)+,a0    Pop return address    0x205F
  438.          *        addq.l        #0x8,a7        Clear stack params    0x508F
  439.          *        jmp            (a0)        Return to caller    0x4ED0
  440.          *        dc.w        0            Fake MacsBug name    0x0000
  441.          *        ** AuditRecord starts here **
  442.          *    };
  443.          */
  444. #if kAuditMagicHeader_0 != 0x4E560000
  445.     << error, the following won't work: see AuditDCMD.c >>
  446. #endif
  447.         recordPtr[0] = 0x4E560000;
  448.         recordPtr[1] = 0x41FA0016;
  449.         recordPtr[2] = 0x226E0008;
  450.         recordPtr[3] = 0x2288426E;
  451.         recordPtr[4] = 0x00104E5E;
  452.         recordPtr[5] = 0x205F508F;
  453.         recordPtr[6] = 0x4ED00000;        /* Pad with fake MacsBug name    */
  454. #if kAuditMagicHeaderSize != 7
  455.     << error, the above won't work: see AuditDCMD.c >>
  456. #endif
  457.         /*
  458.          * If this machine has a data/instruction cache, flush it so that we
  459.          * cannot execute stale data.
  460.          */
  461.         if (TrapAvailable(_HWPriv)) {
  462.             FlushDataCache();
  463.             FlushInstructionCache();
  464.         }
  465.         /*
  466.          * Add this as a gestalt. Errors present problems: either this was already
  467.          * added (no big deal), or the luser is trying to redefine a "normal"
  468.          * Gestalt. We check by trying to get the AuditPtr and hope that
  469.          * GetAuditPtr's internal checks ferret out an attempt to redefine a
  470.          * normal Gestalt.
  471.          */
  472. #ifdef UNIVERSAL_HEADERS
  473.         status = NewGestalt(gestaltSelector, (SelectorFunctionUPP) recordPtr);
  474. #else
  475.         status = NewGestalt(gestaltSelector, (ProcPtr) recordPtr);
  476. #endif
  477.         if (status == gestaltDupSelectorErr) {
  478.             /*
  479.              * Trouble! this has already been added! Your program
  480.              * may eventually crash if the gestalt is not an audit
  481.              * record: see GetAuditPtr() for the safety checks.
  482.              */
  483.             DisposPtr((Ptr) auditPtr);
  484.             auditPtr = GetAuditPtr(gestaltSelector);
  485.             goto exit;
  486.         }
  487.         else if (status != noErr) {
  488.             /*
  489.              * Something is seriously out of order. Just return NULL.
  490.              */
  491.             DisposPtr((Ptr) recordPtr);
  492.             auditPtr = NULL;
  493.             goto exit;
  494.         }
  495.         /*
  496.          * Setup the rest of the AuditRecord.
  497.          */
  498.         LOG.version.u.low = kAuditEarliestVersion;
  499.         LOG.version.u.high = kAuditLatestVersion;
  500.         LOG.recordSize = kAuditRecordSize;
  501.         /*
  502.          * Store the top memory location in the AuditRecord. This helps protect
  503.          * us from crashes in Audit if the caller passes a garbage StringPtr.
  504.          */
  505.         Gestalt(gestaltLogicalRAMSize, (long *) &LOG.logicalRAMSize);
  506.         GetDateTime(&LOG.timeAtStart);
  507.         LOG.ticksAtStart = TickCount();
  508.         LOG.PSN.lowLongOfPSN = kNoProcess;
  509.         /*
  510.          * Ok, so far. Now build the initial free queue.
  511.          */
  512.         queueEntryPtr = &LOG.entries[0];
  513.         for (i = 0; i < nEntries; i++) {
  514.             PutQueueEntry(&LOG.free.queue, queueEntryPtr);
  515.             ++queueEntryPtr;
  516.         }
  517. exit:    EnableAudit(auditPtr, initiallyEnabled);
  518.         PreserveAudit(auditPtr, preserveFirst);
  519.         return (auditPtr);
  520. }
  521.  
  522. /*
  523.  * This function logs data if logging is enabled.
  524.  *
  525.  *        auditPtr    As returned by InitAudit. If NULL, nothing is logged.
  526.  *        idCode        A user-controlled value, by convention an OSType (4-byte
  527.  *                    character), that identifies this entry. The display program
  528.  *                    prints it.
  529.  *        format        The format of the remaining data: kAuditFormatString if the
  530.  *                    datum is a pascal string, otherwise, it is as created by the
  531.  *                    AuditFormat macro.
  532.  *        ...            Additional data as needed. These values must be cast to
  533.  *                    longwords to prevent ThinkC/MPW incompatibilities.
  534.  */
  535. void
  536. Audit(
  537.         register AuditPtr        auditPtr,
  538.         OSType                    idCode,
  539.         unsigned long            format,
  540.         ...
  541.     )
  542. {
  543.         unsigned short                saveSR;
  544.         va_list                        argPtr;
  545.         register unsigned long        *destPtr;
  546.         unsigned long                tickCount;
  547.         unsigned short                maxString;
  548.         unsigned short                thisFormat;
  549.         StringPtr                    theString;
  550.         unsigned char                hexString[10];
  551.         register AuditQueueEntryPtr    entryPtr;
  552.         register AuditQueueEntryPtr    nextPtr;
  553.  
  554.         tickCount = TickCount();
  555.         if (auditPtr != NULL && (LOG.flags & kAuditEnabledMask) != 0) {
  556.             entryPtr = GetQueueEntry(&LOG.free.queue);
  557.             /*
  558.              * Note: *** in the left-most column indicates code that is executed
  559.              * with interrupts disabled.
  560.              */
  561.             saveSR = DisableInterrupts();
  562. /***/        if (entryPtr == NULL) {
  563. /***/            ++LOG.lostData;
  564. /***/            EnableInterrupts(saveSR);
  565.                 if ((LOG.flags & kAuditPreserveFirstMask) == 0) {
  566.                     /*
  567.                      * We really want to log the latest entry. Remove the first
  568.                      * "to be displayed" entry. If successful, it will get the new
  569.                      * record and the next "to be displayed" entry, if any, gets
  570.                      * the lost data counter.
  571.                      */
  572.                     entryPtr = GetQueueEntry(&LOG.data.queue);
  573.                     if (entryPtr == NULL)
  574.                         ;                /* goto return        */
  575.                     else {
  576. /***/                    saveSR = DisableInterrupts();
  577. /***/                    LOG.lostData += ENTRY.lostData;
  578. /***/                    nextPtr = (AuditQueueEntryPtr) LOG.data.queue.qHead;
  579. /***/                    if (nextPtr != NULL) {
  580. /***/                        nextPtr->theEntry.lostData += LOG.lostData;
  581. /***/                        LOG.lostData = 0;
  582. /***/                    }
  583. /***/                    goto returnThisEntry;
  584.                     }
  585.                 }
  586.             }
  587. /***/        else {
  588. returnThisEntry:
  589. /***/            ENTRY.lostData = LOG.lostData;
  590. /***/            LOG.lostData = 0;
  591. /***/            EnableInterrupts(saveSR);
  592.                 ENTRY.tickCount = tickCount;
  593.                 ENTRY.idCode = idCode;
  594.                 ENTRY.format = format;
  595.                 va_start(argPtr, format);
  596.                 destPtr = ENTRY.data;
  597.                 maxString = sizeof (ENTRY.data);
  598.                 while (maxString > 0) {
  599.                     thisFormat = format & kAuditFormatMask;
  600.                     switch (thisFormat) {
  601.                     case kAuditFormatEnd:
  602.                         goto exitLoop;
  603.                     case kAuditFormatString:
  604.                         theString = va_arg(argPtr, StringPtr);
  605.                         /*
  606.                          * Make sure this is a real string pointer. Otherwise, if
  607.                          * it's bogus, theString[0] will cause an address error.
  608.                          */
  609.                         theString = (StringPtr) StripAddress((Ptr) theString);
  610.                         if (theString == NULL
  611.                          || (unsigned long) theString >= LOG.logicalRAMSize) {
  612.                             short                i;
  613.                             unsigned short        value;
  614.  
  615.                             /*
  616.                              * Bogus string pointer (NULL or otherwise extreme):
  617.                              * store the Hex address in the audit record.
  618.                              */
  619.                             for (i = 10; i > 1; i--) {
  620.                                 value = ((unsigned long) theString) & 0x0F;
  621.                                 *((unsigned long *) &theString) >>= 4;
  622.                                 hexString[i] = (value < 10)
  623.                                             ? value + '0' : value + ('A' - 10);
  624.                             }
  625.                             hexString[1] = '?';
  626.                             hexString[0] = 9;
  627.                             theString = hexString;
  628.                         }
  629.                         StoreString(
  630.                             theString,
  631.                             theString[0],
  632.                             maxString,
  633.                             (StringPtr) destPtr
  634.                         );
  635.                         goto exitLoop;
  636.                     case kAuditFormatLocation:
  637.                         GetCallerName(destPtr, maxString, GetA6());
  638.                         goto exitLoop;
  639.                     default:
  640.                         *destPtr++ = va_arg(argPtr, unsigned long);
  641.                         format >>= kAuditFormatShift;
  642.                         maxString -= sizeof (unsigned long);
  643.                         break;
  644.                     }
  645.                 }
  646. exitLoop:        va_end(nextArg);
  647.                 PutQueueEntry(&LOG.data.queue, entryPtr);
  648.                 if (LOG.PSN.highLongOfPSN != 0
  649.                  || LOG.PSN.lowLongOfPSN != kNoProcess)
  650.                     (void) WakeUpProcess(&LOG.PSN);
  651.             }
  652.         }
  653. #undef DATA
  654. }
  655.  
  656. /*
  657.  * The following functions are normally called by the display application:
  658.  */
  659.  
  660. /*
  661.  * Return a pointer to the log area, or NULL if there is none. Note that we make
  662.  * a few sanity checks to better ensure that the gestaltSelector actually refers
  663.  * to a Audit record.
  664.  */
  665. AuditPtr
  666. GetAuditPtr(
  667.         OSType                    gestaltSelector
  668.     )
  669. {
  670.         auto long                gestaltResponse;
  671.         register AuditPtr        auditPtr;
  672.  
  673.         if (TrapAvailable(_Gestalt) == FALSE
  674.          || Gestalt(gestaltSelector, &gestaltResponse) != noErr
  675.          || gestaltResponse == 0
  676.          || (gestaltResponse & 0x3) != 0)
  677.              auditPtr = NULL;
  678.         else {
  679.             auditPtr = (AuditPtr) gestaltResponse;
  680.             /*
  681.              * Sanity check: make sure the result is really an Audit Record and
  682.              * we can deal with the library release that created it.
  683.              */
  684.             if (((unsigned long *) auditPtr)[-kAuditMagicHeaderSize]
  685.                     != kAuditMagicHeader_0
  686.              || LOG.version.u.low < kAuditEarliestVersion
  687.              || LOG.version.u.high > kAuditLatestVersion
  688.              || LOG.recordSize != kAuditRecordSize)
  689.                 auditPtr = NULL;
  690.         }
  691.         return (auditPtr);
  692. }
  693.  
  694. /*
  695.  * Awaken a specified process. Call by the following sequence:
  696.  *        GetCurrentProcess(&oldPSN);        // process to awaken
  697.  *        WakeUpAudit(auditPtr, &oldPSN);
  698.  *    The previous process is now stored in oldPSN)
  699.  *        ... display the log ...
  700.  *        WakeUpAudit(auditPtr, &oldPSN);    // restore old
  701.  *        ExitToShell();
  702.  */
  703. void
  704. WakeUpAudit(
  705.         register AuditPtr        auditPtr,
  706.         ProcessSerialNumber        *oldPSN
  707.     )
  708. {
  709.         ProcessSerialNumber        previousPSN;
  710.         short                    saveSR;
  711.         long                    gestaltResult;
  712.         OSErr                    status;
  713.         
  714.         if (auditPtr != NULL) {
  715.             status = Gestalt(gestaltOSAttr, &gestaltResult);
  716.             if (status != noErr
  717.              || (gestaltResult & (1 << gestaltLaunchControl)) == 0) {
  718.                 oldPSN->highLongOfPSN = 0;
  719.                 oldPSN->lowLongOfPSN = kNoProcess;
  720.             }
  721. /***/        saveSR = DisableInterrupts();
  722. /***/        previousPSN = LOG.PSN;
  723. /***/        LOG.PSN = *oldPSN;
  724. /***/        EnableInterrupts(saveSR);
  725.             *oldPSN = previousPSN;
  726.         }
  727. }
  728.  
  729. /*
  730.  * Enable/disable logging. Returns the old logging state.
  731.  */
  732. Boolean
  733. EnableAudit(
  734.         register AuditPtr        auditPtr,
  735.         Boolean                    enableLogging
  736.     )
  737. {
  738.         Boolean                    oldLogState;
  739.         short                    saveSR;
  740.         
  741.         if (auditPtr == NULL)
  742.             oldLogState = FALSE;
  743.         else {
  744. /***/        saveSR = DisableInterrupts();
  745. /***/        oldLogState = (LOG.flags & kAuditEnabledMask) != 0;
  746. /***/        LOG.flags &= ~kAuditEnabledMask;
  747. /***/        if (enableLogging)
  748. /***/            LOG.flags |= kAuditEnabledMask;
  749. /***/        EnableInterrupts(saveSR);
  750.         }
  751.         return (oldLogState);
  752. }
  753.  
  754. /*
  755.  * Return the value of the Audit enable flag. Returns FALSE if auditPtr is NULL
  756.  * or auditing is disabled.
  757.  */
  758. Boolean
  759. IsAuditEnabled(
  760.         AuditPtr                auditPtr
  761.     )
  762. {
  763.         if (auditPtr == NULL)
  764.             return (FALSE);
  765.         else {
  766.             return ((LOG.flags & kAuditEnabledMask) != 0);
  767.         }
  768. }
  769.  
  770. /*
  771.  * Enable/disable logging. Returns the old logging state.
  772.  */
  773. Boolean
  774. PreserveAudit(
  775.         register AuditPtr        auditPtr,
  776.         Boolean                    preserveFirst
  777.     )
  778. {
  779.         Boolean                    oldPreserveState;
  780.         short                    saveSR;
  781.         
  782.         if (auditPtr == NULL)
  783.             oldPreserveState = FALSE;
  784.         else {
  785. /***/        saveSR = DisableInterrupts();
  786. /***/        oldPreserveState = (LOG.flags & kAuditPreserveFirstMask) != 0;
  787. /***/        LOG.flags &= ~kAuditPreserveFirstMask;
  788. /***/        if (preserveFirst)
  789. /***/            LOG.flags |= kAuditPreserveFirstMask;
  790. /***/        EnableInterrupts(saveSR);
  791.         }
  792.         return (oldPreserveState);
  793. }
  794.  
  795. /*
  796.  * Get the time that the log record was created. This is used to time-stamp log
  797.  * entries. Since the time-stamp is created only when the log is created (and
  798.  * before it is visible through Gestalt), we don't need to lock out interrupts.
  799.  */
  800. void
  801. GetAuditStartTimes(
  802.         register AuditPtr        auditPtr,
  803.         unsigned long            *timeAtStart,
  804.         unsigned long            *ticksAtStart
  805.     )
  806. {
  807.         if (auditPtr == NULL) {
  808.             *timeAtStart = 0;
  809.             *ticksAtStart = 0;
  810.         }
  811.         else {
  812.             *timeAtStart = LOG.timeAtStart;
  813.             *ticksAtStart = LOG.ticksAtStart;
  814.         }
  815. }
  816.  
  817. /*
  818.  * Read the next log entry. This returns a copy of the log entry, if one is
  819.  * available and returns TRUE. This function manages all log queues. If no entry
  820.  * is available, or logging is disabled, the function returns FALSE.
  821.  */
  822. Boolean
  823. ReadAudit(
  824.         register AuditPtr        auditPtr,
  825.         AuditEntryPtr            thisAuditEntry
  826.     )
  827. {
  828.         register AuditQueueEntryPtr    entryPtr;
  829.         
  830.         if (auditPtr == NULL)
  831.             return (FALSE);
  832.         else {
  833.             entryPtr = GetQueueEntry(&LOG.data.queue);
  834.             if (entryPtr == NULL)
  835.                 return (FALSE);
  836.             else {
  837.                 *thisAuditEntry = ENTRY;
  838.                 PutQueueEntry(&LOG.free.queue, entryPtr);
  839.                 return (TRUE);
  840.             }
  841.         }
  842. }
  843.  
  844. /*
  845.  * Store a user-controlled reference value. This may be coerced to any scalar
  846.  * value (such as a memory pointer or longword). SetAuditRefNum returns the
  847.  * current value of the refNum, or zero if auditPtr is NULL.
  848.  */
  849. void
  850. *SetAuditRefNum(
  851.         AuditPtr                auditPtr,
  852.         void                    *refNum
  853.     )
  854. {
  855.         void                *result;
  856.         short                    saveSR;
  857.         
  858.         if (auditPtr == NULL)
  859.             result = NULL;
  860.         else {
  861. /***/        saveSR = DisableInterrupts();
  862. /***/        result = LOG.refNum;
  863. /***/        LOG.refNum = refNum;
  864. /***/        EnableInterrupts(saveSR);
  865.         }
  866.         return (result);
  867. }
  868.  
  869. /*
  870.  * Return the current user-controlled reference value. This may be coerced to any
  871.  * scalar value (such as a memory pointer or longword). This returns zero if
  872.  * auditPtr is NULL or no value had been stored.
  873.  */
  874. void *
  875. GetAuditRefNum(
  876.         AuditPtr                auditPtr
  877.     )
  878. {
  879.         /*
  880.          * Note that we assume that we can retrieve LOG.refNum without
  881.          * interference from interrupt routines. Is this reasonable?
  882.          */
  883.         return ((auditPtr == NULL) ? NULL : LOG.refNum);
  884. }
  885.  
  886. /*
  887.  * Remove the first entry from the queue, return NULL on failure.
  888.  */
  889. static AuditQueueEntryPtr
  890. GetQueueEntry(
  891.         QHdrPtr                    theQueue
  892.     )
  893. {
  894.         register QElemPtr        qElemPtr;
  895.         
  896.         if ((qElemPtr = theQueue->qHead) != NULL) {
  897.             if (Dequeue(qElemPtr, theQueue) != noErr)
  898.                 qElemPtr = NULL;
  899.         }
  900.         return ((AuditQueueEntryPtr) qElemPtr);
  901. }
  902.  
  903. /*
  904.  * TrapAvailable (see Inside Mac VI 3-8)
  905.  */
  906. #define NumToolboxTraps() (                                \
  907.         (NGetTrapAddress(_InitGraf, ToolTrap)            \
  908.                 == NGetTrapAddress(0xAA6E, ToolTrap))    \
  909.             ? 0x200 : 0x400                                \
  910.     )
  911. #define GetTrapType(theTrap) (                            \
  912.         ((theTrap) & 0x800 != 0) ? ToolTrap : OSTrap    \
  913.     )
  914.  
  915. static Boolean
  916. TrapAvailable(
  917.         short                    theTrap
  918.     )
  919. {
  920.         TrapType                trapType;
  921.         
  922.         trapType = GetTrapType(theTrap);
  923.         if (trapType == ToolTrap) {
  924.             theTrap &= 0x07FF;
  925.             if (theTrap >= NumToolboxTraps())
  926.                 theTrap = _Unimplemented;
  927.         }
  928.         return (
  929.             NGetTrapAddress(theTrap, trapType)
  930.             != NGetTrapAddress(_Unimplemented, ToolTrap)
  931.         );
  932. }
  933.  
  934. /*
  935.  * GetCallerName peeks up the calling chain list to the Audit function caller. It
  936.  * then scans through the function code to locate the start and end of the
  937.  * function. The end of the function is followed by the MacsBug name (maybe).
  938.  * This is based on ShowStackChain.c by Greg Anderson greggor@apple.com
  939.  */
  940. static void
  941. GetCallerName(
  942.         unsigned long            *destPtr,
  943.         unsigned short            maxString,
  944.         unsigned long            *a6
  945.     )
  946. {
  947.         unsigned long                returnAddress;
  948.         register unsigned short        *pc;
  949.         short                        maxSearch;
  950.         unsigned short                length;
  951.         Str31                        tempString;
  952.         unsigned long                pcOffset;
  953. #define pcByte        ((unsigned char *) pc)
  954.  
  955.         returnAddress = a6[1];            /* Return address to Audit caller    */
  956.         /*
  957.          * First, look forwards for the symbol name
  958.          */
  959.         pc = (unsigned short *) returnAddress;
  960.         tempString[0] = 0;
  961.         for (maxSearch = 0; maxSearch < 0x4000; maxSearch++, pc++) {
  962.             if (pc[0] == 0x4E56)        /* link                                */
  963.                 break;                    /* We've gone too far                */
  964.             if (pc[0] == 0x4E75            /* rts                                */
  965.               || pc[0] == 0x4ED0        /* jmp (a0)                            */
  966.               || pc[0] == 0x4E74) {        /* rtd #N                            */
  967.                   if (pc[0] == 0x4E74)    /* If rtd, skip over displacement    */
  968.                     pc++;
  969.                 pc++;                    /* Skip over return instruction        */
  970.                  if (pc[0] != 0x4E56) {    /* Link instruction means no name    */
  971.                      /*
  972.                       * pc now points to the word following the RTS. Use the
  973.                       * MacsBug naming conventions to determine the function name,
  974.                       * if any. See Appendix D in the MacsBug User's Guide.
  975.                       */
  976.                       length = pcByte[0] & 0x7F;
  977.                       if (length >= 0x20 && length <= 0x7F) {
  978.                          /*
  979.                           * Fixed length format: first byte is in the range 0x20
  980.                           * through 0x7F, the high bit may or may not be set.
  981.                           */
  982.                           if ((pcByte[1] & 0x80) != 0) {
  983.                               /*
  984.                                * Pascal 16-byte class name format. The string is
  985.                                * stored "method" "class" (each takes 8 bytes) --
  986.                                * MacsBug swaps the order and inserts a '.'
  987.                                * (Warning, this is untested.)
  988.                                */
  989.                               BlockMove(&pcByte[0], &tempString[10], 8);
  990.                               BlockMove(&pcByte[7], &tempString[1], 8);
  991.                               tempString[10] &= ~0x80;
  992.                               tempString[11] &= ~0x80;
  993.                               tempString[9] = '.';
  994.                               tempString[0] = 17;
  995.                           }
  996.                           else {
  997.                               /*
  998.                                * Pascal 8 byte format.
  999.                                */
  1000.                               BlockMove(&pcByte[0], &tempString[1], 8);
  1001.                               tempString[0] = 7;
  1002.                           }
  1003.                     }
  1004.                     else if (pcByte[0] >= 0x80 && pcByte[0] <= 0x9F) {
  1005.                         /*
  1006.                          * Variable-length string format. If the length byte is
  1007.                          * zero after removing the flag bit, the next byte has
  1008.                          * the true (Str255) length.
  1009.                          */
  1010.                         if (length == 0) {
  1011.                             /*
  1012.                              * Step over the flag. Note that pc and pcByte are the
  1013.                              * same variable (pc is a "short *" while pcByte is a
  1014.                              * "char *"). The next statement adds 1 to pc in a way
  1015.                              * that doesn't cause ANSI compilers to lose their
  1016.                              * electronic lunches.
  1017.                              */
  1018.                             pc = (unsigned short *) &pcByte[1];
  1019.                             length = pcByte[0];
  1020.                         }
  1021.                         StoreString(
  1022.                             pcByte,
  1023.                             length,
  1024.                             sizeof (tempString) - 1,
  1025.                             tempString
  1026.                         );
  1027.                     }
  1028.                     else {
  1029.                         /*
  1030.                          * Something isn't understandable. Do nothing. This will
  1031.                          * be caught by the "if there is no symbol" test below.
  1032.                          */
  1033.                     }
  1034.                 }
  1035.                 break;
  1036.             }
  1037.         }
  1038.         /*
  1039.          * If there is no symbol name, don't search for an offset.
  1040.          */
  1041.         pcOffset = 0;
  1042.         if (tempString[0] != 0) {
  1043.             pc = (unsigned short *) returnAddress;
  1044.             for (maxSearch = 0; maxSearch < 0x4000; maxSearch++) {
  1045.                 if (pc[0] == 0x4E56) {                /* link instruction    */
  1046.                     pcOffset =
  1047.                         ((unsigned long) returnAddress)
  1048.                         - ((unsigned long) pc);
  1049.                     break;
  1050.                 }
  1051.                 --pc;
  1052.             }
  1053.         }
  1054.         if (pcOffset == 0 || tempString[0] == 0) {
  1055.             /*
  1056.              * Since we don't have a MacsBug name, set the offset value to zero
  1057.              * (this will act as a signal) and store  the return address in the
  1058.              * second data value.
  1059.              */
  1060.             if (maxString >= sizeof (unsigned long)) {
  1061.                 *destPtr++ = 0;
  1062.                 maxString -= sizeof (unsigned long);
  1063.             }
  1064.             if (maxString >= sizeof (unsigned long)) {
  1065.                 *destPtr++ = returnAddress;
  1066.                 maxString -= sizeof (unsigned long);
  1067.             }
  1068.         }
  1069.         else {
  1070.             if (maxString >= sizeof (unsigned long)) {
  1071.                 *destPtr++ = pcOffset;
  1072.                 maxString -= sizeof (unsigned long);
  1073.                 StoreString(
  1074.                     tempString,
  1075.                     tempString[0],
  1076.                     maxString,
  1077.                     (StringPtr) destPtr
  1078.                 );
  1079.             }
  1080.         }
  1081. #undef pcByte
  1082. }
  1083.  
  1084. /*
  1085.  * Copy the source string to the destination, respecting the maximum size.
  1086.  */
  1087. static void
  1088. StoreString(
  1089.         const StringPtr        theString,
  1090.         unsigned short        stringLength,
  1091.         unsigned short        maxLength,
  1092.         StringPtr            result
  1093.     )
  1094. {
  1095.         if (stringLength >= maxLength)
  1096.             stringLength = maxLength - 1;
  1097.         BlockMove(theString, result, stringLength + 1);
  1098.         result[0] = stringLength;
  1099. }
  1100.             
  1101.  
  1102.